#pragma once

#include "njm_device.hpp"
#include "njm_buffer.hpp"
#include <memory>
#include <vector>
#include <string>
#include <unordered_map>

#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>


namespace njm {

    class NjmModel
    {
    public:
        using id_t = unsigned int;
        using Map = std::unordered_map<id_t, std::shared_ptr<NjmModel>>;

        struct Vertex {
            glm::vec3 position{};
            glm::vec3 color{};
            glm::vec3 normal{};
            glm::vec2 uv{};
            static std::vector<VkVertexInputBindingDescription> getBindingDescriptions();
            static std::vector<VkVertexInputAttributeDescription> getAttributeDescriptions();
            bool operator==(const Vertex &other) const {
                return position == other.position && color == other.color && normal == other.normal && uv == other.uv;
            }
        };
        struct Builder {
            std::vector<Vertex> vertices{};
            std::vector<uint32_t> indices{};
            void loadModel(const std::string& filepath);
        };

        NjmModel(NjmDevice& device, const NjmModel::Builder &builder);
        ~NjmModel();
        NjmModel(const NjmModel &) = delete;
        NjmModel &operator=(const NjmModel &) = delete;

        static std::unique_ptr<NjmModel> createModelFromFile(NjmDevice& device, const std::string& filepath);

        void bind(VkCommandBuffer commandBuffer);
        void draw(VkCommandBuffer commandBuffer);

        id_t getId() const { return id; }
        void setId(id_t id) { this->id = id; }
        std::string getName() const { return name; }
        void setName(const std::string& name) { this->name = name; }

        uint32_t getVertexCount() const { return vCnt; }
        uint32_t getIndexCount() const { return iCnt; }
        
    private:
        NjmDevice& njmDevice;
        id_t id{};
        std::string name{};

        std::unique_ptr<NjmBuffer> vBuf;
        uint32_t vCnt;

        bool hasIndexBuffer = false;
        std::unique_ptr<NjmBuffer> iBuf;
        uint32_t iCnt;

        void createVertexBuffers(const std::vector<Vertex> &vertices);
        void createIndexBuffers(const std::vector<uint32_t> &indices);
    };
    
}